#include "atlasparser.h"
#include "SDL2/SDL.h"

AtlasParser::AtlasParser(std::string path)
	: file {path}
{}

AtlasParser::~AtlasParser()
{
	if (file.is_open())
		file.close();
}

bool AtlasParser::dry_run()
{
	bool has_dimensions = false;
	bool has_sprite = false;
	AtlasToken token { AtlasToken::INVALID };
	AtlasToken last { AtlasToken::INVALID };

	for (std::string line; getline(file, line);) {
		if (token != AtlasToken::EMPTY && token != AtlasToken::COMMENT)
			last = token;

		token = match_token(line);

		if (token == AtlasToken::EMPTY || token == AtlasToken::COMMENT)
			continue;

		if (token == AtlasToken::INVALID) {
			SDL_Log("parsing error: invalid token encountered: %s\n", line.c_str());
			return false;
		}

		if (last == AtlasToken::DESCRIPTOR_DIMENSIONS && token != AtlasToken::DIMENSIONS) {
			SDL_Log("parsing error: dimensions block <int w, int h> has to follow a dimensions descriptor: %s\n", line.c_str());
			return false;
		}

		if (last == AtlasToken::DESCRIPTOR_SPRITE && (token != AtlasToken::SPRITE && token != AtlasToken::SPRITE_MANY)) {
			SDL_Log("parsing error: sprite <str name, bool animated, int frames, int duration> has to follow a sprite descriptor: %s\n", line.c_str());
			return false;
		}

		if (token == AtlasToken::BREAK && (last != AtlasToken::SPRITE && last != AtlasToken::SPRITE_MANY)) {
			SDL_Log("parsing error: can only break after sprites: %s\n", line.c_str());
			return false;
		}

		has_dimensions = has_dimensions || token == AtlasToken::DIMENSIONS;
		has_sprite = has_sprite || (token == AtlasToken::SPRITE || token == AtlasToken::SPRITE_MANY);
	}

	file.clear();
	file.seekg(0);

	return has_dimensions && has_sprite;
}

std::vector<Sprite> AtlasParser::parse()
{
	std::vector<Sprite> sprites;
	AtlasToken token { AtlasToken::INVALID };
	AtlasToken last { AtlasToken::INVALID };
	int w = 0, h = 0, x = 0, y = 0;

	for (std::string line; getline(file, line);) {
		if (token != AtlasToken::EMPTY && token != AtlasToken::COMMENT)
			last = token;

		token = match_token(line);

		if (token == AtlasToken::EMPTY || token == AtlasToken::COMMENT)
			continue;

		if (token == AtlasToken::BREAK) {
			x = 0;
			y += h;
			continue;
		}

		if (token == AtlasToken::DIMENSIONS) {
			std::istringstream line_stream(line);
			line_stream >> w >> h;
			continue;
		}

		if (token == AtlasToken::SPRITE) {
			std::istringstream line_stream(line);
			int frames, duration;
			bool animated;
			line_stream >> std::boolalpha >> animated >> frames >> duration;

			Sprite sprite;
			sprite.src.x = x;
			sprite.animation_x_offset = x;
			sprite.src.y = y;
			sprite.animated = animated;
			sprite.frames = frames;
			sprite.duration = duration;
			sprite.src.w = w;
			sprite.src.h = h;

			if (animated) {
				for (int i = 0; i < frames; i++) {
					sprites.push_back(sprite);
					x += w;
				}
			} else {
				sprites.push_back(sprite);
				x += w;
			}

			continue;
		}

		if (token == AtlasToken::SPRITE_MANY) {
			std::istringstream line_stream(line);
			int n, frames, duration;
			bool animated;
			line_stream >> n >> std::boolalpha >> animated >> frames >> duration;

			for (int i = 0; i < n; i++) {
				Sprite sprite;
				sprite.src.x = x;
				x += w;
				sprite.src.y = y;
				sprite.animated = animated;
				sprite.frames = frames;
				sprite.duration = duration;
				sprite.src.w = w;
				sprite.src.h = h;

				sprites.push_back(sprite);
			}

			continue;
		}
	}

	return sprites;
}

AtlasToken AtlasParser::match_token(std::string line)
{
	for (auto& matcher : matchings)
		if (regex_search(line, mode, matcher.first))
			return matcher.second;

	return AtlasToken::INVALID;
}